package com.sunmouse.toolkit.widget.menu;

import android.content.Context;
import android.os.SystemClock;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import com.sunmouse.toollib.utils.LogUtils;
import com.sunmouse.toolkit.R;
import com.sunmouse.toolkit.widget.DragHelper;

/**
 * Created by Administrator on 2017/8/16.
 */
public class DraggingLayout extends ViewGroup {
    private DragHelper mDragHelper;
    private MenuDragCallback menuDragCallback;
    private FrameLayout menuLayout;
    private FrameLayout contentLayout;

    private final static int MIN_DRAG_MARGIN = 64;
    private final static int MIN_VELOCITY = 400;
    private int dragMargin;
    private float minVelocity;

    private boolean mChildrenCanceledTouch;
    private boolean hasPeeked;

    private float offset;
    private float ds;


    public DraggingLayout(Context context) {
        this(context, null);
    }

    public DraggingLayout(Context context, AttributeSet attrs) {
        this(context, null, 0);
    }

    public DraggingLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        contentLayout = new FrameLayout(context);
        contentLayout.setBackgroundResource(R.color.colorAccent);
        contentLayout.setLayoutParams(generateLayoutParams(0));
        addView(contentLayout);
        menuLayout = new FrameLayout(context);
        menuLayout.setBackgroundResource(R.color.colorPrimary);
        menuLayout.setLayoutParams(generateLayoutParams(0));
        addView(menuLayout);

        float density = getResources().getDisplayMetrics().density;
        dragMargin = (int) (MIN_DRAG_MARGIN * density + 0.5f);
        minVelocity = MIN_VELOCITY * density;
        menuDragCallback = new MenuDragCallback();
//        menuDragCallback = new ViewDragCallback(Gravity.LEFT);

        mDragHelper = DragHelper.create(this, 1.0f, menuDragCallback);
        mDragHelper.setEdgeTrackingEnabled(DragHelper.EDGE_LEFT);
        menuDragCallback.setDrag(mDragHelper);
        mDragHelper.setMinVelocity(minVelocity);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        LogUtils.d("---onMeasure---------mw=" + menuLayout.getWidth());
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        LogUtils.d("----------changed=" + changed);
        if (!changed) return;
        int w = getWidth();
        int h = getHeight();
        menuLayout.layout(-w + dragMargin, h / 4, 0, h * 3 / 4);
        contentLayout.layout(0, 0, w, h);
        LogUtils.d("---onLayout---------mw=" + menuLayout.getWidth());

    }


    @Override
    public void computeScroll() {
        if (mDragHelper.continueSettling(true))
            ViewCompat.postInvalidateOnAnimation(this);
//        super.computeScroll();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mDragHelper.processTouchEvent(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mChildrenCanceledTouch = false;
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                closeDrawer();
                break;
        }
        return true;
    }

    /**
     * close drawer
     */
    private void closeDrawer() {
        LogUtils.d("---------------closeDrawer");
        boolean invalidateFlag = false;
        if (hasPeeked) {
            hasPeeked = false;
            int left = -menuLayout.getWidth();
            int top = menuLayout.getTop();
            invalidateFlag |= mDragHelper.smoothSlideViewTo(menuLayout, left, top);
        }
        menuDragCallback.removeCallbacks();//remove cks
        if (invalidateFlag)
            invalidate();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean mDragIntercept = mDragHelper.shouldInterceptTouchEvent(ev);

        switch (ev.getAction()) {
            case MotionEvent.ACTION_MOVE:
                if (mDragHelper.checkTouchSlop(DragHelper.DIRECTION_ALL)) {
                    menuDragCallback.removeCallbacks();
                }
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                closeDrawer();
                break;
        }
//        mDragHelper.shouldInterceptTouchEvent(ev);
        return mDragIntercept || hasPeeked;
    }

    @Override
    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        super.requestDisallowInterceptTouchEvent(disallowIntercept);
        if (disallowIntercept) {
            closeDrawer();
        }
    }

    /**
     * drag callback
     */
    private class MenuDragCallback extends DragHelper.Callback {

        private DragHelper mDrag;

        public void setDrag(DragHelper mDrag) {
            this.mDrag = mDrag;
        }

        /**
         * remove ck
         */
        private void removeCallbacks() {
            DraggingLayout.this.removeCallbacks(mPeekRunnable);
        }

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return child == menuLayout;
        }

        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            int childWidth = changedView.getWidth();
            if (changedView == menuLayout) {
                offset = (float) (childWidth + left) / childWidth;
            } else {
                offset = (float) (getWidth() - left) / childWidth;
            }
            changedView.setVisibility(offset == 0 ? INVISIBLE : VISIBLE);

            invalidate();

            ViewCompat.offsetLeftAndRight(contentLayout, dx);
            ds += (float) dx / 888 / 2;
            LogUtils.d("----------------ds=" + ds);
            ViewCompat.setScaleY(changedView, 1 + ds * 2);
            ViewCompat.setScaleY(contentLayout, 1 - ds);
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            int left = 0;
            if (releasedChild == menuLayout) {
                left = (xvel > 0.0f || xvel == 0.0f && offset > 0.5f) ? 0 : -releasedChild.getWidth();
//                LogUtils.d("-------------offset=" + offset + ":left=" + left + ":mw=" + releasedChild.getWidth());
            } else {
                final int width = getWidth();
                left = xvel < 0 || xvel == 0 && offset > 0.5f ? width - releasedChild.getWidth() : width;
            }
            boolean res = mDrag.settleCapturedViewAt(left, releasedChild.getTop());

            if (left == releasedChild.getWidth()) {
                  ViewCompat.offsetLeftAndRight(contentLayout,0);
            } else if (left == 0) {

            }
            invalidate();

        }

        @Override
        public int getViewHorizontalDragRange(View child) {
//            LogUtils.d("dragRange--" + (child == menuLayout) + "--" + child.getWidth());
            return menuLayout == child ? child.getWidth() : 0;
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            int clamp;
            if (child == menuLayout)
                clamp = Math.max(-child.getWidth(), Math.min(left, 0));
            else
                clamp = Math.max(getWidth() - child.getWidth(), Math.min(left, getWidth()));
//            LogUtils.d("---------------clamp=" + clamp);
            return clamp;
        }


        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            return child.getTop();
        }

        @Override
        public void onEdgeTouched(int edgeFlags, int pointerId) {
            postDelayed(mPeekRunnable, 160);
        }

        @Override
        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
            mDrag.captureChildView(menuLayout, pointerId);
            hasPeeked = false;
        }

        private final Runnable mPeekRunnable = new Runnable() {
            @Override
            public void run() {
                peekDrawer();
            }
        };

        private void peekDrawer() {
//            LogUtils.d("---------------peekDrawer");
            int dL = -menuLayout.getWidth() + mDrag.getEdgeSize();
            int dT = menuLayout.getTop();
            mDrag.smoothSlideViewTo(menuLayout, dL, dT);
            hasPeeked = true;
            invalidate();

            cancelChildViewTouch();
        }


    }

    void cancelChildViewTouch() {
        // Cancel child touches
//        LogUtils.d("------------cancelChildViewTouch");
        if (!mChildrenCanceledTouch) {
            final long now = SystemClock.uptimeMillis();
            final MotionEvent cancelEvent = MotionEvent.obtain(now, now,
                    MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
            final int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                getChildAt(i).dispatchTouchEvent(cancelEvent);
            }
            cancelEvent.recycle();
            mChildrenCanceledTouch = true;
//            LogUtils.d("------------cancelChild");
        }
    }


    /**
     * generate default layout param;
     */
    private LayoutParams generateLayoutParams(int index) {
        LayoutParams lp;
        switch (index) {
            default:
            case 0:
                lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
                break;
            case 1:
                lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
                break;
            case 2:
                lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
                break;
        }
        return lp;
    }


    private class ViewDragCallback extends DragHelper.Callback {
        private final int mAbsGravity;
        private DragHelper mDragger;

        private final Runnable mPeekRunnable = new Runnable() {
            @Override
            public void run() {
                peekDrawer();
            }
        };

        ViewDragCallback(int gravity) {
            mAbsGravity = gravity;
        }

        public void setDrag(DragHelper drag) {
            mDragger = drag;
        }

        public void removeCallbacks() {
            DraggingLayout.this.removeCallbacks(mPeekRunnable);
        }

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            // Only capture views where the gravity matches what we're looking for.
            // This lets us use two ViewDragHelpers, one for each side drawer.
            return child == menuLayout;
        }


        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            float offset;
            final int childWidth = changedView.getWidth();
//            LogUtils.d("-------v=" + changedView.hashCode() + "-left=" + left + "-top=" + top + "-dx=" + dx + "-dy=" + dy);
            // This reverses the positioning shown in onLayout.
            if (changedView == menuLayout) {
                offset = (float) (childWidth + left) / childWidth;
            } else {
                final int width = getWidth();
                offset = (float) (width - left) / childWidth;
            }
            changedView.setVisibility(offset == 0 ? INVISIBLE : VISIBLE);
            invalidate();
        }


        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            // Offset is how open the drawer is, therefore left/right values
            // are reversed from one another.
            LogUtils.d("------onViewReleased");
            final int childWidth = releasedChild.getWidth();

            int left;
            if (releasedChild == menuLayout) {
                LogUtils.d("-----offset=" + offset);
                left = xvel > 0 || xvel == 0 && offset > 0.5f ? 0 : -childWidth;
            } else {
                final int width = getWidth();
                left = xvel < 0 || xvel == 0 && offset > 0.5f ? width - childWidth : width;
            }

            boolean res = mDragger.settleCapturedViewAt(left, releasedChild.getTop());
            LogUtils.d("------------res=" + res);
            invalidate();
        }

        @Override
        public void onEdgeTouched(int edgeFlags, int pointerId) {
//            LogUtils.d("------onEdgeTouched");
            postDelayed(mPeekRunnable, 160);
        }

        void peekDrawer() {
            final View toCapture;
            final int childLeft;
            final int peekDistance = mDragger.getEdgeSize();
            final boolean leftEdge = mAbsGravity == Gravity.LEFT;
            if (leftEdge) {
                toCapture = menuLayout;
                childLeft = (toCapture != null ? -toCapture.getWidth() : 0) + peekDistance;
            } else {
                toCapture = menuLayout;
                childLeft = getWidth() - peekDistance;
            }
            // Only peek if it would mean making the drawer more visible and the drawer isn't locked
            if (toCapture != null && ((leftEdge && toCapture.getLeft() < childLeft)
                    || (!leftEdge && toCapture.getLeft() > childLeft))
                    ) {
                final LayoutParams lp = (LayoutParams) toCapture.getLayoutParams();
//                LogUtils.d("------v=" + toCapture.hashCode() + ":rL=" + toCapture.getLeft() + ":L=" + childLeft + ":T=" + toCapture.getTop());
                mDragger.smoothSlideViewTo(toCapture, childLeft, toCapture.getTop());
                invalidate();


                cancelChildViewTouch();
            }
        }


        @Override
        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
            LogUtils.d("------onEdgeDragStarted");
            final View toCapture;
//            if ((edgeFlags & ViewDragHelper.EDGE_LEFT) == ViewDragHelper.EDGE_LEFT) {
//                toCapture = findDrawerWithGravity(Gravity.LEFT);
//            } else {
//                toCapture = findDrawerWithGravity(Gravity.RIGHT);
//            }
////            LogUtils.d("------v=" + toCapture.hashCode() + ":Id=" + pointerId);
//            if (toCapture != null && getDrawerLockMode(toCapture) == LOCK_MODE_UNLOCKED) {
//                // mDragger.captureChildView(toCapture, pointerId);
//            }
        }

        @Override
        public int getViewHorizontalDragRange(View child) {
            return child == menuLayout ? child.getWidth() : 0;
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            int clamp;
            if (child == menuLayout) {
                clamp = Math.max(-child.getWidth(), Math.min(left, 0));
//                LogUtils.d("------------Horizontal" + clamp);
            } else {
                final int width = getWidth();
                clamp = Math.max(width - child.getWidth(), Math.min(left, width));
            }
//            LogUtils.d("------------clamp=" + clamp);
            return clamp;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            return child.getTop();
        }
    }
}
